package com.eedi.framework.errorcode.service;

import cn.hutool.core.collection.CollUtil;
import com.eedi.framework.errorcode.controller.sys.vo.*;
import com.google.common.annotations.VisibleForTesting;
import com.eedi.framework.common.pojo.PageResult;
import com.eedi.framework.common.util.object.BeanUtils;
import com.eedi.framework.errorcode.controller.sys.vo.*;
import com.eedi.framework.errorcode.enums.SysErrorCodeTypeEnum;
import com.eedi.framework.errorcode.dal.dataobject.SysErrorCodeDO;
import com.eedi.framework.errorcode.dal.mapper.ErrorCodeMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

import static com.eedi.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.eedi.framework.common.util.collection.CollectionUtils.convertMap;
import static com.eedi.framework.common.util.collection.CollectionUtils.convertSet;
import static com.eedi.framework.errorcode.enums.SysErrorErrorCodeConstants.*;

/**
 * 错误码 Service 实现类
 *
 * @author dlyan
 */
@Service
@Validated
@Slf4j
public class SysErrorCodeServiceImpl implements SysErrorCodeService {

    @Resource
    private ErrorCodeMapper errorCodeMapper;

    @Override
    public String createErrorCode(SysErrorCodeSaveReq createReqVO) {
        // 校验 code 重复
        validateCodeDuplicate(createReqVO.getSysErrorCode(), null);

        // 插入
        SysErrorCodeDO errorCode = BeanUtils.toBean(createReqVO, SysErrorCodeDO.class);
        errorCode.setSysErrorCodeType(SysErrorCodeTypeEnum.MANUAL_OPERATION);
        errorCodeMapper.insert(errorCode);
        // 返回
        return errorCode.getSysErrorCodeId();
    }

    @Override
    public void updateErrorCode(SysErrorCodeUpReq updateReqVO) {
        // 校验存在
        validateErrorCodeExists(updateReqVO.getSysErrorCodeId());
        // 校验 code 重复
        validateCodeDuplicate(updateReqVO.getSysErrorCode(), updateReqVO.getSysErrorCodeId());

        // 更新
        SysErrorCodeDO updateObj = BeanUtils.toBean(updateReqVO, SysErrorCodeDO.class);
        updateObj.setSysErrorCodeType(SysErrorCodeTypeEnum.MANUAL_OPERATION);
        errorCodeMapper.updateById(updateObj);
    }

    @Override
    public void deleteErrorCode(String id) {
        // 校验存在
        validateErrorCodeExists(id);
        // 删除
        errorCodeMapper.deleteById(id);
    }

    /**
     * 校验错误码的唯一字段是否重复
     *
     * 是否存在相同编码的错误码
     *
     * @param code 错误码编码
     * @param id 错误码编号
     */
    @VisibleForTesting
    public void validateCodeDuplicate(Integer code, String id) {
        SysErrorCodeDO sysErrorCodeDO = errorCodeMapper.selectByCode(code);
        if (sysErrorCodeDO == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的错误码
        if (id == null) {
            throw exception(ERROR_CODE_DUPLICATE);
        }
        if (!sysErrorCodeDO.getSysErrorCodeId().equals(id)) {
            throw exception(ERROR_CODE_DUPLICATE);
        }
    }

    @VisibleForTesting
    void validateErrorCodeExists(String id) {
        if (errorCodeMapper.selectById(id) == null) {
            throw exception(ERROR_CODE_NOT_EXISTS);
        }
    }

    @Override
    public SysErrorCodeDO getErrorCode(String id) {
        return errorCodeMapper.selectById(id);
    }

    @Override
    public PageResult<SysErrorCodeDO> getErrorCodePage(SysErrorCodePageReq pageReqVO) {
        return errorCodeMapper.selectPage(pageReqVO);
    }

    @Override
    public SysErrorCodeResp getByCode(Integer sysErrorCode) {
        return errorCodeMapper.getByCode(sysErrorCode);
    }

    @Override
    @Transactional
    public void autoGenerateErrorCodes(List<SysErrorCodeAutoGenerateReq> autoGenerateDTOs) {
        if (CollUtil.isEmpty(autoGenerateDTOs)) {
            return;
        }
        // 获得错误码
        List<SysErrorCodeDO> sysErrorCodeDOS = errorCodeMapper.selectListByCodes(
                convertSet(autoGenerateDTOs, SysErrorCodeAutoGenerateReq::getSysErrorCode));
        Map<Integer, SysErrorCodeDO> errorCodeDOMap = convertMap(sysErrorCodeDOS, SysErrorCodeDO::getSysErrorCode);

        // 遍历 autoGenerateBOs 数组，逐个插入或更新。考虑到每次量级不大，就不走批量了
        autoGenerateDTOs.forEach(autoGenerateDTO -> {
            SysErrorCodeDO errorCode = errorCodeDOMap.get(autoGenerateDTO.getSysErrorCode());
            // 不存在，则进行新增
            if (errorCode == null) {
                errorCode = BeanUtils.toBean(autoGenerateDTO, SysErrorCodeDO.class);
                errorCode .setSysErrorCodeType(SysErrorCodeTypeEnum.AUTO_GENERATION);
                errorCodeMapper.insert(errorCode);
                return;
            }
            // 存在，则进行更新。更新有三个前置条件：
            // 条件 1. 只更新自动生成的错误码，即 Type 为 ErrorCodeTypeEnum.AUTO_GENERATION
            if (!SysErrorCodeTypeEnum.AUTO_GENERATION.equals(errorCode.getSysErrorCodeType())) {
                return;
            }
            // 条件 2. 分组 applicationName 必须匹配，避免存在错误码冲突的情况
            if (!autoGenerateDTO.getSysErrorCodeAppName().equals(errorCode.getSysErrorCodeAppName())) {
                log.error("[autoGenerateErrorCodes][自动创建({}/{}) 错误码失败，数据库中已经存在({}/{})]",
                        autoGenerateDTO.getSysErrorCode(), autoGenerateDTO.getSysErrorCodeAppName(),
                        errorCode.getSysErrorCode(), errorCode.getSysErrorCodeAppName());
                throw exception(ERROR_CODE_DUPLICATE,errorCode.getSysErrorCode());
            }
            // 条件 3. 错误提示语存在差异
            if (autoGenerateDTO.getSysErrorMessage().equals(errorCode.getSysErrorMessage())) {
                throw exception(ERROR_CODE_TIPS_DISCREPANCY,autoGenerateDTO.getSysErrorMessage(),errorCode.getSysErrorMessage());
            }
            // 最终匹配，进行更新
            SysErrorCodeDO sysErrorCodeDO = new SysErrorCodeDO();
            sysErrorCodeDO.setSysErrorMessage(autoGenerateDTO.getSysErrorMessage());
            errorCodeMapper.updateById(sysErrorCodeDO);
        });
    }

    @Override
    public List<SysErrorCodeSimpleResp> getErrorCodeList(String applicationName, LocalDateTime minUpdateTime) {
        List<SysErrorCodeDO> list = errorCodeMapper.selectListByApplicationNameAndUpdateTimeGt(
                applicationName, minUpdateTime);
        return BeanUtils.toBean(list, SysErrorCodeSimpleResp.class);
    }

}

